/*
 * Copyright 2011 Christoph Ludwig (christoph[at]chludwig.de). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.chludwig.ehumanities.provenance.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Auxiliary class for packaging directories as zip archives.
 */
public class ZipUtil {

  private static class PrefixPathStripper {

    private int prefixLength;

    public PrefixPathStripper(File prefixPath) {
      prefixLength = prefixPath.getPath().length() + 1;
    }

    public String makeRelative(File path) {
      return path.getPath().substring(prefixLength);
    }
  }

  private int compressionLevel;

  private final StreamUtil streamUtil = new StreamUtil();

  public ZipUtil() {
    this.compressionLevel = 0;
  }

  /**
   * @return the compressionLevel
   */
  public int getCompressionLevel() {
    return compressionLevel;
  }

  public void setCompressionLevel(int compressionLevel) {
    this.compressionLevel = compressionLevel;
  }

  public void archiveRecursively(File sourceFile, File targetFile) throws IOException {
    sourceFile = sourceFile.getAbsoluteFile();
    File baseDir = sourceFile.isFile() ? sourceFile.getParentFile() : sourceFile;
    ZipOutputStream targetZipStream = new ZipOutputStream(new FileOutputStream(targetFile));
    try {
      try {
        targetZipStream.setMethod(ZipOutputStream.DEFLATED);
        targetZipStream.setLevel(getCompressionLevel());
        archiveRecursively(new PrefixPathStripper(baseDir), sourceFile, targetZipStream);
      }
      finally {
        targetZipStream.close();
      }
    }
    catch (IOException ex) {
      targetFile.delete();
      throw ex;
    }
  }

  private void archiveRecursively(PrefixPathStripper prefixPathStripper, File sourceFile, ZipOutputStream targetStream)
          throws IOException {
    List<File> files = FileUtil.getAllFiles(sourceFile);
    List<File> subdirectories = FileUtil.getAllDirectories(sourceFile);
    for (File elem : files) {
      InputStream elemInputStream = new BufferedInputStream(new FileInputStream(elem));
      try {
        ZipEntry zipEntry = new ZipEntry(prefixPathStripper.makeRelative(elem));
        targetStream.putNextEntry(zipEntry);
        streamUtil.transfer(elemInputStream, targetStream);
      }
      finally {
        elemInputStream.close();
      }
    }
    for (File subdir : subdirectories) {
      archiveRecursively(prefixPathStripper, subdir, targetStream);
    }
  }


}
